﻿namespace System.ServiceModel.DomainServices.Client
{
    using System;
    using System.Globalization;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Runtime.CompilerServices;

    public static class EntityQueryable
    {
        public static EntityQuery<TEntity> OrderBy<TEntity, TKey>(this EntityQuery<TEntity> source, Expression<Func<TEntity, TKey>> keySelector) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (keySelector == null)
            {
                throw new ArgumentNullException("keySelector");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, Queryable.OrderBy<TEntity, TKey>(source.QueryRoot, keySelector));
        }

        public static EntityQuery<TEntity> OrderByDescending<TEntity, TKey>(this EntityQuery<TEntity> source, Expression<Func<TEntity, TKey>> keySelector) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (keySelector == null)
            {
                throw new ArgumentNullException("keySelector");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, Queryable.OrderByDescending<TEntity, TKey>(source.QueryRoot, keySelector));
        }

        public static EntityQuery<TEntity> Select<TEntity>(this EntityQuery<TEntity> source, Expression<Func<TEntity, TEntity>> selector) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (selector == null)
            {
                throw new ArgumentNullException("selector");
            }
            return new EntityQuery<TEntity>(source, Queryable.Select<TEntity, TEntity>(source.QueryRoot, selector));
        }

        public static EntityQuery<TEntity> Skip<TEntity>(this EntityQuery<TEntity> source, int count) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, source.QueryRoot.Skip<TEntity>(count));
        }

        public static EntityQuery<TEntity> Take<TEntity>(this EntityQuery<TEntity> source, int count) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, source.QueryRoot.Take<TEntity>(count));
        }

        public static EntityQuery<TEntity> ThenBy<TEntity, TKey>(this EntityQuery<TEntity> source, Expression<Func<TEntity, TKey>> keySelector) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (keySelector == null)
            {
                throw new ArgumentNullException("keySelector");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, Queryable.ThenBy<TEntity, TKey>((IOrderedQueryable<TEntity>) source.QueryRoot, keySelector));
        }

        public static EntityQuery<TEntity> ThenByDescending<TEntity, TKey>(this EntityQuery<TEntity> source, Expression<Func<TEntity, TKey>> keySelector) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (keySelector == null)
            {
                throw new ArgumentNullException("keySelector");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, Queryable.ThenByDescending<TEntity, TKey>((IOrderedQueryable<TEntity>) source.QueryRoot, keySelector));
        }

        public static EntityQuery<TEntity> Where<TEntity>(this EntityQuery<TEntity> source, Expression<Func<TEntity, bool>> predicate) where TEntity: Entity
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            if (predicate == null)
            {
                throw new ArgumentNullException("predicate");
            }
            if (!source.IsComposable)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.EntityQuery_NotComposable, new object[] { typeof(TEntity).Name, source.QueryName }));
            }
            return new EntityQuery<TEntity>(source, Queryable.Where<TEntity>(source.QueryRoot, predicate));
        }
    }
}

